package com.demo2do.fms.admin.service.business.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.entity.page.Page;
import com.demo2do.core.entity.result.HttpResult;
import com.demo2do.core.http.annotation.Rao;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.GenericJdbcSupport;
import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.fms.admin.dao.business.DispatchOrderDao;
import com.demo2do.fms.admin.dao.business.DispatchOrderDaoSupport;
import com.demo2do.fms.admin.dao.business.OtherFeeDaoSupport;
import com.demo2do.fms.admin.dao.business.SequenceDaoSupport;
import com.demo2do.fms.admin.dao.business.ShipmentDao;
import com.demo2do.fms.admin.dao.business.ShipmentLineDao;
import com.demo2do.fms.admin.dao.business.ShipmentOrderDao;
import com.demo2do.fms.admin.dao.data.FacilityDistanceDao;
import com.demo2do.fms.admin.dao.data.RepairFeeDao;
import com.demo2do.fms.admin.dao.data.TyreFeeDao;
import com.demo2do.fms.admin.dao.supplier.EmployeeDao;
import com.demo2do.fms.admin.dao.vehicle.VehicleDao;
import com.demo2do.fms.admin.entity.report.business.ShipmentReportSearch;
import com.demo2do.fms.admin.entity.search.business.ShipmentSearch;
import com.demo2do.fms.admin.entity.security.Login;
import com.demo2do.fms.admin.handler.support.FmsJdbcSupport;
import com.demo2do.fms.admin.rao.ShipmentRao;
import com.demo2do.fms.admin.service.business.ShipmentService;
import com.demo2do.fms.entity.business.Sequences;
import com.demo2do.fms.entity.business.Shipment;
import com.demo2do.fms.entity.business.balance.OtherFee;
import com.demo2do.fms.entity.business.shipment.DispatchOrder;
import com.demo2do.fms.entity.business.shipment.ShipmentLine;
import com.demo2do.fms.entity.business.shipment.ShipmentOrder;
import com.demo2do.fms.entity.business.shipment.ShipmentOrderItem;
import com.demo2do.fms.entity.data.business.Warehouse;
import com.demo2do.fms.entity.data.cost.DefaultOilType;
import com.demo2do.fms.entity.data.cost.RepairFee;
import com.demo2do.fms.entity.data.cost.TyreFee;
import com.demo2do.fms.entity.driver.DriverDuty;
import com.demo2do.fms.entity.driver.Employee;
import com.demo2do.fms.entity.vehicle.source.Vehicle;

/**
 * 
 * @author Ysy
 *
 */
@Service("shipmentService")
public class ShipmentServiceImpl implements ShipmentService {
	
	@Value("#{config['shipment.electronic.url']}")
	private String electronicUrl;
    
    private static final Logger logger = LoggerFactory.getLogger(ShipmentServiceImpl.class);
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private GenericJdbcSupport genericJdbcSupport;
    
    @Autowired
    private FmsJdbcSupport fmsJdbcSupport;
    
    @Autowired
    private DispatchOrderDaoSupport dispatchOrderDaoSupport;
    
    @Autowired
    private SequenceDaoSupport sequenceDaoSupport;
    
    @Autowired
    private OtherFeeDaoSupport otherFeeDaoSupport;
    
    @Dao
    private ShipmentDao shipmentDao;
    
    @Dao
    private ShipmentLineDao shipmentLineDao;
    
    @Dao
    private DispatchOrderDao dispatchOrderDao;
    
    @Dao
    private EmployeeDao employeeDao;
    
    @Dao
    private FacilityDistanceDao facilityDistanceDao;
    
    @Dao
    private RepairFeeDao repairFeeDao;
    
    @Dao
    private TyreFeeDao tyreFeeDao;
    
    @Dao
    private VehicleDao vehicleDao;
    
    @Dao
    private ShipmentOrderDao shipmentOrderDao;
    
    @Rao
    private ShipmentRao shipmentRao;
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#countShipment(com.demo2do.fms.admin.entity.search.business.ShipmentSearch)
     */
    public int countShipment(ShipmentSearch shipmentSearch) {
        
        String sentence = shipmentSearch.buildSentenceTemplate();
        Map<String, Object> parameters = shipmentSearch.buildParameters();
        
        // out by shipment search criteria
        return genericDaoSupport.count(sentence, parameters);
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#listShipment(com.demo2do.fms.admin.entity.search.business.ShipmentSearch, com.demo2do.core.entity.page.Page)
     */
    @SuppressWarnings("unchecked")
    public List<ShipmentLine> listShipment(ShipmentSearch shipmentSearch, Page page) {
        
        String sentence = shipmentSearch.buildSentenceTemplate();
        Map<String, Object> parameters = shipmentSearch.buildParameters();
        
        List<ShipmentLine> shipmentLines = genericDaoSupport.searchForList(sentence, parameters, page.getBeginIndex(), page.getEveryPage());
        page.calculate();
        
        return shipmentLines;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#validateDispatchOrderGenerate(java.lang.String)
     */
    public void validateDispatchOrderGenerate(String loadNumber) {
        
        // 1. load shipment_line by load_number
        ShipmentLine shipmentLine = shipmentLineDao.load(loadNumber);
        
        // 2. check the dispatch_order if exist
        String loadGroupNumber = shipmentLine.getShipmentGroup();
        DispatchOrder dispatchOrder = dispatchOrderDao.loadByLoadGroupNumber(loadGroupNumber);
        
        // 3. 对生成派车单时，所必须的基础数据项进行校验 （去除了老系统原本对司机与油价设置的校验。）
        if(dispatchOrder == null) {  

            // 3.0 实际进行运输业务的运输公司
            Long orgId = shipmentLine.getTransCorpId();
            
            // 3.1 校验驳运车的有效性，并获取驳运车的动力类型
            Long vehicleId = shipmentLine.getTransMeansId();
            Vehicle vehicle = genericDaoSupport.get(Vehicle.class, vehicleId);
            if(vehicle == null) {
                throw new BusinessException("驳运车状态有误");
            }
            
            // 3.2 get vehicle_feature_id
            Long vehicleFeatureId = vehicle.getVehicleFeature().getId();
            
            // 3.3 validate repair fee
            List<RepairFee> repairFees = repairFeeDao.list(orgId, vehicleFeatureId);  //该运输公司对该种动力类型的修理费
            if(CollectionUtils.isEmpty(repairFees)) { 
                throw new BusinessException("请设置运输公司该动力类型的修理费");
            }
            
            // 3.4 validate tyre fee 
            List<TyreFee> tyreFees = tyreFeeDao.list(orgId, vehicleFeatureId);  //该运输公司对该种动力类型的轮胎费
            if(CollectionUtils.isEmpty(tyreFees)) { 
                throw new BusinessException("请设置运输公司该动力类型的轮胎费");
            }
            
            //3.5 validate facility
            Warehouse startWarehouse = genericDaoSupport.get(Warehouse.class, shipmentLine.getSrcWhId()); 
            Warehouse destinationWarehouse = genericDaoSupport.get(Warehouse.class, shipmentLine.getDestWhId()); 
            if(startWarehouse == null || destinationWarehouse == null) {
                throw new BusinessException("装卸地状态有误");
            }

            // 3.6 validate facility distance
            BigDecimal facilityDistance = facilityDistanceDao.load(orgId, startWarehouse.getLoadPlaceId(), destinationWarehouse.getLoadPlaceId()); // 行驶公里数
            if(facilityDistance == null){
                throw new BusinessException("请设置行驶公里数");
            }
            
            // 3.7 validate default oil type
            DefaultOilType defaultOilType = genericDaoSupport.get(DefaultOilType.class, orgId);
            if (defaultOilType == null) {
                throw new BusinessException("请设置运输公司默认加油类型");
            }
            
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#preGenerateDispatchOrder(com.demo2do.fms.entity.business.ShipmentLine)
     */
    public DispatchOrder preGenerateDispatchOrder(ShipmentLine shipmentLine) {
        
        // 1. get all data
        Long orgId = shipmentLine.getTransCorpId(); // 实际进行运输业务的运输公司
        
        // 获取仓库，并验证仓库的有效性
        Warehouse startWarehouse = genericDaoSupport.get(Warehouse.class, shipmentLine.getSrcWhId()); 
        
        Warehouse destinationWarehouse = genericDaoSupport.get(Warehouse.class, shipmentLine.getDestWhId()); 
        
        String loadGroupNumber = shipmentLine.getShipmentGroup(); // 调度指令组号
        
        Long startFacilityId = startWarehouse.getLoadPlaceId(); //起始地
        
        Long destinationFacilityId = destinationWarehouse.getLoadPlaceId(); //目的地
        
        Long vehicleId = shipmentLine.getTransMeansId(); // 驳运车ID
        
        Long mainDriverId = employeeDao.loadEmployeeId(vehicleId, DriverDuty.MAIN_DRIVER.getId()); //主驾驶ID
        
        Long firstViceDriverId = employeeDao.loadEmployeeId(vehicleId, DriverDuty.VICE_DRIVER.getId()); //副驾驶ID
        
        BigDecimal facilityDistance = facilityDistanceDao.load(orgId, startFacilityId, destinationFacilityId); // 行驶公里数
        
        Integer fullLoadCount = vehicleDao.getFullLoadCount(vehicleId); // 装载能力
        
        // 2. new dispatch_order
        DispatchOrder dispatchOrder = new DispatchOrder(new Long(-1), orgId, loadGroupNumber, startFacilityId, destinationFacilityId, vehicleId, 
                mainDriverId, firstViceDriverId, facilityDistance, fullLoadCount);
        
        return dispatchOrder;
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#generateDispatchOrder(com.demo2do.fms.entity.business.shipment.DispatchOrder, java.lang.String, java.lang.Long, com.demo2do.fms.admin.entity.security.Login)
     */
    public Long generateDispatchOrder(DispatchOrder dispatchOrder, String loadNumber, Long oilTypeId, Login login) {
        
        // validate driver_number
        Integer driverNumber = this.validateDriver(dispatchOrder);
        
        // init dispatch order code
        this.initDispatchOrderCode(dispatchOrder);
        
        try {
            
            // 调用存储过程
            Long dispatchOrderId = dispatchOrderDaoSupport.generate(dispatchOrder, loadNumber, oilTypeId, driverNumber, login.getId(), login.getName());
            return dispatchOrderId;
            
        } catch (Exception e) {
            logger.error("#generateDispatchOrder() - call store procedure occur exception [{}]", e);
            throw BusinessException.newInstance("500", "生成派车单失败");
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#generateUpdateDispatchOrder(com.demo2do.fms.entity.business.shipment.DispatchOrder, java.lang.String, java.lang.Long, java.math.BigDecimal, com.demo2do.fms.entity.business.shipment.TransportCost, java.util.List, java.util.List, com.demo2do.fms.admin.entity.security.Login)
     */
    public Long generateUpdateDispatchOrder(DispatchOrder dispatchOrder, String loadNumber, Long oilTypeId, BigDecimal adjustmentDistance, BigDecimal roadFee, List<Long> otherFeeTypeIds,	List<BigDecimal> otherFees, Login login, String otherFeeAuditor) {
    	
    	// validate driver_number
        Integer driverNumber = this.validateDriver(dispatchOrder);
        
        // init dispatch order code
        this.initDispatchOrderCode(dispatchOrder);
        
        try {
        	
        	BigDecimal otherFeeSum = calculateOtherFee(otherFees);
        	
            // 2. count the facility distance by adjustment_distance
            BigDecimal facilityDistance = dispatchOrder.getFacilityDistanceDefault();
            facilityDistance = facilityDistance.add(adjustmentDistance);
        	
            // 调用存储过程
            Long dispatchOrderId = dispatchOrderDaoSupport.generateUpdate(dispatchOrder, loadNumber, oilTypeId, driverNumber, roadFee, facilityDistance, otherFeeSum, login.getId(), login.getName(), otherFeeAuditor);
            
            // 创建 other fee
            this.editOtherFee(otherFeeTypeIds, otherFees, dispatchOrderId);
            
            return dispatchOrderId;
            
        } catch (Exception e) {
            logger.error("#generateDispatchOrder() - call store procedure occur exception [{}]", e);
            throw BusinessException.newInstance("500", "生成派车单失败");
        }
        
    }
    
    /**
     * edit other_fee
     *
     * @param otherFees
     * @param dispatchOrderId
     */
    private void editOtherFee(List<Long> otherFeeTypeIds, List<BigDecimal> otherFees, Long dispatchOrderId) {
        
        List<Long> allOtherFeeTypeIds = otherFeeDaoSupport.listOtherFeeTypeIds();
        
        for (Long feeTypeId : allOtherFeeTypeIds) {
            
            // 1. 获取该费用类型的费用
            BigDecimal fee = otherFees.get( otherFeeTypeIds.indexOf(feeTypeId) );
            
            fee = (fee == null ? BigDecimal.ZERO : fee);
            
            // 2. 获取该派车单下的，该种其他费用类型，并且不是被安吉设置的其他费用
            OtherFee oldOtherFee = otherFeeDaoSupport.load(feeTypeId, dispatchOrderId, false);
            
            if(oldOtherFee == null){ // 3.0 若不存在，创建
                
                OtherFee otherFee = new OtherFee(feeTypeId, dispatchOrderId, feeTypeId, false, fee);
                genericDaoSupport.save(otherFee);
                
            } else{ // 3.1 若存在，修改费用
                genericDaoSupport.update(oldOtherFee.editOtherFee(fee));
            }
            
        }
        
    }
    
    /**
     * 
     * @param otherFees
     * @return
     */
    private BigDecimal calculateOtherFee(List<BigDecimal> otherFees) {
    	
    	BigDecimal otherFeeSum = BigDecimal.ZERO;
        for (BigDecimal otherFee : otherFees) {
            
            if(otherFee == null) {
                continue;
            }
            
            otherFeeSum = otherFeeSum.add(otherFee);
            
        }
        
        return otherFeeSum;
    	
    }
    
    
    /**
     * 
     * @param dispatchOrder
     */
    private Integer validateDriver(DispatchOrder dispatchOrder) {
        
        Integer driverNumber = 1;
        
        // validate main_driver 
        Long mainDriverId = dispatchOrder.getMainDriverId();
        Employee mainDriver = genericDaoSupport.load(Employee.class, mainDriverId);
        if(!mainDriver.getActive()) {
            throw new BusinessException("主驾驶员未激活");
        }
        
        // validate first_vice_driver 
        Long firstViceDriverId = dispatchOrder.getFirstViceDriverId();
        if(firstViceDriverId != null && firstViceDriverId != 0L){
            
            Employee firstViceDriver = genericDaoSupport.load(Employee.class, firstViceDriverId);
            
            if(!firstViceDriver.getActive())
                throw new BusinessException("第一副驾驶未激活");
            
            driverNumber++;
        }
        
        return driverNumber;
        
    }

    /**
     * 
     * @param dispatchOrder
     */
    protected void initDispatchOrderCode(DispatchOrder dispatchOrder) {
        
        // if dispatch_order_id is -1, edit code
        if(dispatchOrder.getId() == -1) {
            String dispatchCode = this.getDispatchOrderSequence("DISPATCH_ORDER");
            dispatchOrder.editCode(dispatchCode);
        }
        
    }
    
    /**
     * 
     * @param sequenceType
     * @return
     */
    public String getDispatchOrderSequence(String sequenceType){
        
        String nowYear = DateFormatUtils.yearAsString();    // condition
        
        String sequenceCode = sequenceDaoSupport.getDispatchOrderSequence(sequenceType, nowYear);
        
        logger.info("Dispatch Order Sequence: " + sequenceCode);
        
        if(StringUtils.isBlank(sequenceCode)) { // 新增
            Sequences newSequence = new Sequences(sequenceType, 1L, nowYear);
            sequenceCode = "1";
            genericDaoSupport.save(newSequence);
        } else {    // 更改
        	Long sequence = Long.valueOf(sequenceCode);
        	sequenceCode = String.valueOf(sequence + 1);
            sequenceDaoSupport.updateDispatchOrderSequence(sequenceType, nowYear, sequenceCode);
        }
        
        String blank = "00000000";
        String code = blank + sequenceCode;
        code = nowYear + code.substring(code.length() - blank.length(), code.length());
            
        return code;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#load(java.lang.String)
     */
    public ShipmentLine loadShipmentLine(String loadNumber) {
        return shipmentLineDao.load(loadNumber);
    }

    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#load(java.lang.Long)
     */
    public Shipment loadShipment(Long shipmentId) {
        return shipmentDao.loadByShipmentId(shipmentId);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#loadShipment(java.lang.String, java.util.List)
     */
    public Shipment loadShipment(String loadNumber, List<Long> orgIds) {
        return shipmentDao.load(loadNumber, orgIds);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#listShipmentOrder(java.lang.Long)
     */
    public List<ShipmentOrder> listShipmentOrder(Long dispatchOrderId) {
        
        // 1. 列出派车单下所有调度指令订单
        List<ShipmentOrder> shipmentOrders = shipmentOrderDao.listShipmentOrder(dispatchOrderId);
        
        // 2. 填充每个调度指令订单的商品车信息
        this.initShipmentOrderItems(shipmentOrders);
        
        return shipmentOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#listShipmentOrder(java.lang.String)
     */
    public List<ShipmentOrder> listShipmentOrder(String loadNumber) {
        
        // 1. list shipment_orders by load_number
        List<ShipmentOrder> shipmentOrders = shipmentOrderDao.listShipmentOrder(loadNumber);
        
        // 2. complete shipment_order_items of every shipment_order
        this.initShipmentOrderItems(shipmentOrders);
        
        return shipmentOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#listShipmentOrder(java.lang.Long, java.lang.String)
     */
    public List<ShipmentOrder> listShipmentOrder(Long dispatchOrderId, String loadNumber) {
        
        // 1. list shipment_orders by dispatch_order_id or load_number
        List<ShipmentOrder> shipmentOrders = shipmentOrderDao.listShipmentOrder(dispatchOrderId, loadNumber);
        
        // 2. complete shipment_order_items of every shipment_order
        this.initShipmentOrderItems(shipmentOrders);
        
        return shipmentOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#listShipmentOrder(java.util.List)
     */
    public List<ShipmentOrder> listShipmentOrder(List<Long> dispatchOrderIds) {
        
        // 1. 获取报销派车单的所有调度订单
        List<ShipmentOrder> shipmentOrders = new ArrayList<ShipmentOrder>();
        for (Long dispatchOrderId : dispatchOrderIds) {
            
            String sentence = "SELECT order_id, shipment_number FROM VW_SHIPMENT_ORDER_NO_FINANCE WHERE dispatch_order_id = :dispatchOrderId";
            List<Map<String, Object>> shipmentOrderList = genericJdbcSupport.queryForList(sentence, "dispatchOrderId", dispatchOrderId);
            
            for (Map<String, Object> map : shipmentOrderList) {
                
                BigDecimal orderId = (BigDecimal) map.get("order_id");
                String shipmentNumber = (String) map.get("shipment_number");
                
                ShipmentOrder shipmentOrder = new ShipmentOrder(orderId.longValue(), shipmentNumber, dispatchOrderId);
                shipmentOrders.add(shipmentOrder);
                
            }
            
        }
        
        // 2. 填充每个调度订单的商品车信息
        this.initShipmentOrderItems(shipmentOrders);
        
        return shipmentOrders;
        
    }
    
    /**
     * 
     * @param shipmentOrders
     * @return
     */
    private List<ShipmentOrder> initShipmentOrderItems(List<ShipmentOrder> shipmentOrders){
        
        for(ShipmentOrder shipmentOrder : shipmentOrders) {
            
            Long orderId = shipmentOrder.getId();   // 调度指令订单id
            String shipmentNumber = shipmentOrder.getShipmentNumber();  // 调度指令号
            
            // 1 获取调度指令订单下商品车信息
            List<ShipmentOrderItem> shipmentOrderItems = shipmentOrderDao.listShipmentOrderItem(orderId, shipmentNumber);
            
            // 2.1 设置电子回单信息 对于 ShipmentOrderItem
            for (ShipmentOrderItem shipmentOrderItem : shipmentOrderItems) {
				
            	Map<String, Object> parameters = new HashMap<String, Object>();
            	parameters.put("shipmentId", shipmentOrderItem.getShipmentId());
            	parameters.put("loadNumber", shipmentOrderItem.getShipmentNumber());

            	try{
                    HttpResult httpResult = shipmentRao.searchElectronicConfirm(electronicUrl, parameters);
                    if (httpResult.isValid()) {
                        shipmentOrderItem.setElectronicConfirm(true);
                    }
                }catch (Exception e){
            	    logger.error("#initShipmentOrderItems() Exception [{}]","ERROR");
                }
			}
            
            // 2.2 为调度指令填充商品车信息
            shipmentOrder.initShipmentOrderItems(shipmentOrderItems);
            
        }
        
        return shipmentOrders;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#exportAll(com.demo2do.fms.admin.entity.report.business.ShipmentReportSearch)
     */
    public List<Map<String, Object>> exportAll(ShipmentReportSearch search) {
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        RowMapper<Map<String, Object>> rowMapper = search.buildRowMapper();
        return fmsJdbcSupport.queryForList(sentence, parameters, rowMapper);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.business.ShipmentService#getElectronicUrls(com.demo2do.fms.entity.business.Shipment)
     */
    public HttpResult getElectronicImage(Shipment shipment) {
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        
        parameters.put("loadNumber", shipment.getLoadNumber());
        parameters.put("shipmentId", shipment.getShipmentId());

        try{

            HttpResult httpResult = shipmentRao.searchElectronicConfirm(electronicUrl, parameters);
            logger.info("#getElectronicImage()  - result=[{}]", JsonUtils.toJson(httpResult));
            return httpResult;

        }catch (Exception e){

            logger.error("#getElectronicImage Exception [{}]","ERROR");
        }

        return null;
    }

    
}